#define PrynInternal
#define PrynImplementation
#include <pryn.h>

#define PrynCheckMediator() \
	prynAssert (mediator, NullArgument); \
	state = mediator->pLibrary->pState

// Build a function for a mediator, ensuring it's non-zero before executing body, and assigns state.
#define PrynMediatorBody(BODY) \
	do { \
		PrynStartFunction (); \
		{ \
			PrynCheckMediator (); \
			BODY \
		} \
		PrynEndFunction (); \
	} while (0)

#define PrynMediatorGetProperty(NAME, TYPE, INITIAL, VALUE) \
	PrynExport (PrynResult, prynMediator##NAME, (PrynMediator *mediator, TYPE *output)) \
	{ \
		const PrynString nullString = {0, 0}; \
		if (output) *output = INITIAL; \
		PrynMediatorBody ({ \
			prynAssert (output, NullArgument); \
			*output = VALUE; \
		}); \
	}

PrynMediatorGetProperty (State, PrynState *, 0, state)
PrynMediatorGetProperty (Tags, PrynTagList *, 0, &mediator->pCommonObject.pTags)
PrynMediatorGetProperty (Name, PrynString, nullString, mediator->pCommonResource.pName)
PrynMediatorGetProperty (Description, PrynString, nullString, mediator->pCommonResource.pDescription)
PrynMediatorGetProperty (Library, PrynLibrary *, 0, mediator->pLibrary)

static PrynResult dummyEnable (PrynMediator *self) { return PrynResultSuccess; }

PrynExport (PrynResult, prynMediatorCreate, (PrynMediator **output, PrynLibrary *library, const PrynString *id))
{
	PrynStartFunction ();
	PrynMediator *mediator = 0;

	if (output) *output = 0;
	if (library) state = library->pState;
	prynAssert (library && output && id && id->pointer, NullArgument);
	
	for (mediator = state->pMediators.first; mediator; mediator = mediator->pNext)
		if (!prynStringEqual (&mediator->pId, id))
			break;
		
	if (!mediator)
	{
		prynCall (prynAllocateClearedObject (state, &mediator));
		mediator->pCommonObject.pType = PrynObjectTypeMediator;
		mediator->pLibrary = library;
		mediator->pId = *id;
		mediator->pEnableFunction = &dummyEnable;
		mediator->pEnabled = false;
		prynDListAppendP (state->pMediators, mediator);
	}
	
	mediator->pCommonObject.pMark = true;
	*output = mediator;
	PrynEndFunction ();
}

PrynExport (PrynResult, prynMediatorEnable, (PrynMediator *mediator))
{
	PrynState *state = mediator ? mediator->pLibrary->pState : 0;

	if (!mediator || !mediator->pEnableFunction) return PrynResultNullArgument;
	if (mediator->pEnabled) return PrynResultDone;
	mediator->pEnabled = true;
	return mediator->pEnableFunction (mediator);
}

PrynExport (PrynResult, prynMediatorSetName, (PrynMediator *mediator, const PrynString *value))
{
	PrynMediatorBody ({
		prynAssert (value, NullArgument);
		prynCommonResourceSetName (&mediator->pCommonResource, value);
	});
}

PrynExport (PrynResult, prynMediatorSetDescription, (PrynMediator *mediator, const PrynString *value))
{
	PrynMediatorBody ({
		prynAssert (value, NullArgument);
		prynCommonResourceSetDescription (&mediator->pCommonResource, value);
	});
}

PrynExport (PrynResult, prynMediatorSetEnableFunction, (PrynMediator *mediator, PrynMediatorEnableFunction value))
{
	PrynMediatorBody ({
		mediator->pEnableFunction = value;
	});
}

PrynExport (PrynResult, prynMediatorDestroy, (PrynMediator *mediator))
{
	PrynStartFunction ();
	PrynLibrary *library;
	PrynType *type, *typeNext;
	PrynFactory *factory, *factoryNext;

	PrynCheckMediator ();
	library = mediator->pLibrary;

	prynCommonResourceDestroy (&mediator->pCommonResource, state);
	for (type = state->pTypes.first; type; type = typeNext)
	{
		typeNext = type->pNext;
		if (type->pMediator == mediator)
			prynTypeDestroy (type);
	}

	for (factory = state->pFactories.first; factory; factory = factoryNext)
	{
		factoryNext = factory->pNext;
		if (factory->pMediator == mediator)
			prynFactoryDestroy (factory);
	}

	prynCommonObjectDestroy (&mediator->pCommonObject, state);
	prynDListRemoveP (state->pMediators, mediator);
	prynFree (state, mediator);

	PrynEndFunction ();
}